package metier.repository;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.EJBException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public abstract class AbstractRepository<T> {

    @PersistenceContext
    private EntityManager em;

    private Class entityClass;

    public AbstractRepository() {
    }

    @PostConstruct
    private void initialisation() {
        this.entityClass = ((Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }

    public T persist(T item) {
        if (item == null) {
            throw new EJBException("L'objet ne peut pas être null");
        }
        em.persist(item);
        return item;
    }

    public T update(T item) {
        if (item == null) {
            throw new EJBException("L'objet ne peut pas être null");
        }

        em.merge(item);
        return item;
    }

    public List<T> getAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return em.createQuery(cq).getResultList();
    }

    public List<T> getAllOrderBy(String... orderBy) {
        CriteriaQuery<T> c = em.getCriteriaBuilder().createQuery(entityClass);
        Root<T> from = c.from(entityClass);
        this.addSort(c, from, orderBy);
        TypedQuery<T> query = em.createQuery(c);
        return query.getResultList();
    }

    public T getById(Object id) {
        if (id == null) {
            throw new EJBException("L'identifiant ne peut pas être null");
        }

        return (T) em.find(entityClass, id);
    }

    public void delete(T item) {
        if (item == null) {
            throw new EJBException("L'objet ne peut pas être null");
        }

        em.remove(em.merge(item));
    }

    public List<T> findByAttributes(Map<String, String> attributes) {
        List<T> results;
        // set up the Criteria query
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(getEntityClass());
        Root<T> root = cq.from(getEntityClass());

        List<Predicate> predicates = new ArrayList<Predicate>();
        for (String s : attributes.keySet()) {
            if (root.get(s) != null) {
                predicates.add(cb.like((Expression) root.get(s), "%" + attributes.get(s) + "%"));
            }
        }
        cq.where(predicates.toArray(new Predicate[]{}));
        TypedQuery<T> q = em.createQuery(cq);

        results = q.getResultList();
        return results;
    }

    private void addSort(CriteriaQuery<T> query, Root<T> from, String... orderBy) {
        if (orderBy != null && orderBy.length > 0) {
            List<Order> orders = new ArrayList<Order>();
            for (String orderParameter : orderBy) {
                Order order = em.getCriteriaBuilder().asc(from.get(orderParameter));
                orders.add(order);
            }
            query.orderBy(orders);
        }
    }

    public EntityManager getEm() {
        return em;
    }

    public Class getEntityClass() {
        return entityClass;
    }

}
